#include "utils.h"

#include "CNet.h"
#include "CLayer.h"
#include "CImage.h"

/*----------------------------------------------------------------------------*/

CNet::CNet( int _inputsNum, const IntV& _neuronsNumber )
	:	m_inputsNum( _inputsNum )
	,	m_meanSquareError( 0.0 )
	,	m_currImage( NULL )
{
	m_layers.push_back( new CLayer( m_inputsNum, _neuronsNumber[0] ) );
	size_t i = 1;
	for ( ; i < _neuronsNumber.size() - 1; i++ )
	{
		m_layers.push_back( new CLayer( _neuronsNumber[i-1], _neuronsNumber[i] ) );
	}
	m_layers.push_back( new CLayer( _neuronsNumber[i-1], _neuronsNumber[i], true ) );
}


/*----------------------------------------------------------------------------*/


CNet::CNet( std::ifstream& _file )
	:	m_meanSquareError( 0.0 )
	,	m_currImage( NULL )
{
	_file.read( (char*)&m_inputsNum, sizeof( int ) );
	int layersNum;
	_file.read( (char*)&layersNum, sizeof( int ) );
	for ( int i = 0; i < layersNum - 1; i++ )
	{
		m_layers.push_back( new CLayer( _file ) );
	}
	m_layers.push_back( new CLayer( _file, true ) );
}


/*----------------------------------------------------------------------------*/


CNet::~CNet()
{
	for ( size_t i = 0; i < m_layers.size(); i++ )
	{
		if( m_layers[i] )
			delete m_layers[i];
	}
}


/*----------------------------------------------------------------------------*/


void CNet::Calculate( CImage* _image )
{	
	m_currImage = _image;
	m_layers[0]->CalculateOutputValues( m_currImage->GetData() );
	for( size_t i = 1; i < m_layers.size(); i++ )
	{		
		m_layers[i]->CalculateOutputValues( m_layers[i-1]->GetResultValues() );
	}
}


/*----------------------------------------------------------------------------*/


void CNet::Train( double _speed, CImage* _image )
{
	Calculate( _image );
	CalcLocalErrors();
	CalcMeanSquareError();
	CalcNewWeights( _speed );	
}


/*----------------------------------------------------------------------------*/


void CNet::CalcNewWeights( double _speed )
{
	m_layers[0]->CalcNewWeights( _speed, m_currImage->GetData() );
	for ( size_t i = 1; i < m_layers.size(); i++ )
	{
		m_layers[i]->CalcNewWeights( _speed, m_layers[i-1]->GetResultValues() );
	}
}


/*----------------------------------------------------------------------------*/


void CNet::SaveToFile( std::ofstream& _file )
{
	size_t layersSize = m_layers.size();
	_file.write( (char*)&m_inputsNum, sizeof( int ) );
	_file.write( (char*)&layersSize, sizeof( size_t ) );
	for ( size_t i = 0; i < layersSize; i++)
	{
		m_layers[i]->SaveToFile( _file );
	}	
}


/*----------------------------------------------------------------------------*/


void CNet::PrintStat()
{
	PrintNeuroWeights();
	PrintNeuroOutputs();
	PrintError();
	PrintWeError();
	PrintBackError();
}


/*----------------------------------------------------------------------------*/


void CNet::PrintMeanSquareError( int _iteration )
{
	printf( "\n> CNet: Training...iteration[%.4d]: %f", _iteration, m_meanSquareError );
}


/*----------------------------------------------------------------------------*/


void CNet::PrintNetOutputs( int _imageNum )
{
	printf( "\n> Net: image%d", _imageNum );
	m_layers.back()->PrintNeuroOutputs();
}


/*----------------------------------------------------------------------------*/


void CNet::CalcLocalErrors()
{	
	m_layers.back()->CalcLocalErrors( m_currImage->GetResult() );
	for ( size_t i = m_layers.size() - 1; i > 0; i-- )
	{
		m_layers[i-1]->CalcLocalErrors( m_layers[i]->GetBackErrors() );
	}
}


/*----------------------------------------------------------------------------*/


void CNet::CalcMeanSquareError()
{	
	m_meanSquareError = 0.0;
	for( int i = 0; i < m_layers.back()->GetNeuronsNum(); i++ )
	{
		double desiredOutput = m_currImage->GetResult()[i];
		double actualOutput = m_layers.back()->GetResultValues()[i];
		m_meanSquareError += pow( ( desiredOutput - actualOutput ), 2 );
	}
	m_meanSquareError = 0.5 * m_meanSquareError;
}


/*----------------------------------------------------------------------------*/


void CNet::PrintNeuroWeights()
{
	printf("\n> CNet: Printing neurons weights:");
	for( size_t i = 0; i < m_layers.size(); i++ )
	{
		printf("\n> Layer %d:", i);
		m_layers[i]->PrintNeuroWeights();
	}
}


/*----------------------------------------------------------------------------*/


void CNet::PrintNeuroOutputs()
{
	printf("\n> CNet: Printing neurons outputs:");
	for( size_t i = 0; i < m_layers.size(); i++ )
	{		
		printf("\n> Layer %d:", i);		
		m_layers[i]->PrintNeuroOutputs();
	}
}


/*----------------------------------------------------------------------------*/


void CNet::PrintError()
{
	printf("\n> CNet: Printing neurons local errors:");
	for( size_t i = 0; i < m_layers.size(); i++ )
	{		
		printf("\n> Layer %d:", i);		
		m_layers[i]->PrintError();
	}
}


/*----------------------------------------------------------------------------*/


void CNet::PrintWeError()
{
	printf("\n> CNet: Printing neurons weighted errors:");
	for( size_t i = 0; i < m_layers.size(); i++ )
	{
		printf("\n> Layer %d:", i);
		m_layers[i]->PrintWeError();
	}
}


/*----------------------------------------------------------------------------*/


void CNet::PrintBackError()
{
	printf("\n> CNet: Printing total backward errors:");
	for( size_t i = 0; i < m_layers.size(); i++ )
	{
		printf("\n> Layer %d:", i);
		m_layers[i]->PrintBackError();
	}	
}


/*----------------------------------------------------------------------------*/


double CNet::GetMeanSquareError() const
{
	return m_meanSquareError;
}


/*----------------------------------------------------------------------------*/


int CNet::GetMaxOutput() const
{
	const DoubleV& results = m_layers.back()->GetResultValues();
	int pos = -1;
	double maxVal = 0.0;
	for ( size_t i = 0; i < results.size(); i++ )
	{
		if ( pos == -1 || maxVal < results[i] )
		{
			pos = i;
			maxVal = results[i];
		}
	}
	
	return pos;
}


/*----------------------------------------------------------------------------*/

